/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package cn2;

import java.util.*;
import java.io.*;


/**
 *
 * @author michaellangmayr
 */
public class Overlay {
    
    private ArrayList ownNeighbours = new ArrayList();
    private int listenPort = 0;
    private String localAddress = "";
    RemotePeer rp = null;
    private String repoPath;
    private HashMap searchMap = new HashMap();
    private boolean downloadInProgress = false;
    

    public Overlay(int lp) {
        this.listenPort = lp;

        // create polling timer
        Timer myTimer = new Timer();
        myTimer.schedule(new IsAliveTask(this), 10000, 30000);

    }

    public void setRepoPath(String path) {
        this.repoPath = path;
    }

    public String getRepoPath() {
        return this.repoPath;
    }

    public Overlay(int lp, RemotePeer rp) {
        this.listenPort = lp;

        // create polling timer
        Timer myTimer = new Timer();
        myTimer.schedule(new IsAliveTask(this), 10000, 30000);

        this.rp = rp;
    }

    public ArrayList getOwnNeighbours() {
           return this.ownNeighbours;
    }

    public int getListenPort() {
        return this.listenPort;
    }

    public void Connect() throws Exception {

        if ( this.rp == null) {
            return;
        }

        System.out.println("[Overlay] Connecting to Network with first peer: " + this.rp.toString());

        ArrayList initialPeers = this.rp.getNeighbours();
        ArrayList allKnownPeers = new ArrayList();
        ArrayList tmpPeers;

        allKnownPeers.add(this.rp); // add first peer to knownPeers

        this.addPeersToRootList(initialPeers, allKnownPeers); // add neighbours of first peer to knownPeers
        Iterator itInitial = initialPeers.iterator();
        while(itInitial.hasNext()) {

            tmpPeers = ((RemotePeer)itInitial.next()).getNeighbours();
            this.addPeersToRootList(tmpPeers, allKnownPeers);

            Iterator tmpIt = tmpPeers.iterator();
            while (tmpIt.hasNext()) {
                this.addPeersToRootList(((RemotePeer)tmpIt.next()).getNeighbours(), allKnownPeers);

            }
        }

        Iterator it = allKnownPeers.iterator();
        for(int i=0; it.hasNext() && i < 10; i++)
        {
            RemotePeer myPeer = (RemotePeer)it.next();
            this.registerNeighbour(myPeer);
            // register yourself as neighbour
            try {
                myPeer.registerAsNeighbour(this.listenPort);
            } catch (Exception e) {
                System.out.println("[Overlay] exception while registering at " + this.rp.toString() + " - " + e.toString());
            }
        }
    }

    public void disconnect() {

        Iterator it = this.getOwnNeighbours().iterator();
        while (it.hasNext()) {
            
            RemotePeer rp = ((RemotePeer)it.next());
            try {
                rp.unregisterAsNeighbour(this.listenPort);
            } catch (Exception e) {
                System.out.println("[Overlay] exception while unregistering from " + rp.toString() + " - " + e.toString());
            }
        }

        this.getOwnNeighbours().clear();
    }

    public void pingNeighbours() {

        Iterator it = this.getOwnNeighbours().iterator();
        while (it.hasNext()) {
            RemotePeer rp = (RemotePeer)it.next();
            System.out.println("[Overlay] pinging " + rp.toString() + "...");

            try {
                if (!rp.isAlive()) {
                    System.out.println("[Overlay] " + rp.toString() + " is dead...unregistering");
                    this.unregisterNeighbour(rp);
                } else {
                    System.out.println("[Overlay] " + rp.toString() + " is still alive");
                }
            } catch (Exception e) {
                System.out.println("[Overlay] exception in pingNeighbours: " + e.toString());
            }
        }
    }

    public void PrintNeighbours() {
        System.out.println("[Overlay] List of neighbouring peers:");
        Iterator it = this.getOwnNeighbours().iterator();
        while (it.hasNext()) {
            RemotePeer rp = ((RemotePeer)it.next());
            
            System.out.println("[Overlay] Peer: " + rp.toString());
        }
    }


    public void addPeersToRootList(ArrayList peers, ArrayList knownPeers) {

        RemotePeer tmpPeer;

        Iterator it = peers.iterator();
        while (it.hasNext()) {
            tmpPeer = (RemotePeer)it.next();

            if (!knownPeers.contains(tmpPeer)) {
                knownPeers.add(tmpPeer);
            }
        }
    }

    public void unregisterNeighbour(RemotePeer rPeer) {

        if (this.getOwnNeighbours().contains(rPeer)) {
            this.getOwnNeighbours().remove(rPeer);
            System.out.println("[Overlay] " + rPeer + " unregistered");
        }
    }

    public void registerNeighbour(RemotePeer rPeer) {
        
        if (!this.getOwnNeighbours().contains(rPeer)) {
            this.getOwnNeighbours().add(rPeer);
            System.out.println("[Overlay] " + rPeer + " registered");
        }          
    }

    public ArrayList searchFileNeighbours(String pattern) {

        // calculate random searchID
        Random rand = new Random();
        int randomID = rand.nextInt();
        
        ArrayList results = new ArrayList();
        Iterator it = this.ownNeighbours.iterator();
        while (it.hasNext()) {

            RemotePeer tmpPeer = (RemotePeer)it.next();
            try {
                results.addAll(tmpPeer.search(pattern, this.listenPort, this.localAddress, randomID));
            } catch (Exception e) {
                System.out.println("[Overlay] exception while sending search to neighbour: " + e.toString());
            }                       
        }

        return results;
    }
    
     public ArrayList searchFileNeighbours(String pattern, RemotePeer rPeer, int fwdPort, String fwdAddr, int randomID) {
        ArrayList results = new ArrayList();
        Iterator it = this.ownNeighbours.iterator();
        while (it.hasNext()) {

            RemotePeer tmpPeer = (RemotePeer)it.next();

            if ( !(tmpPeer.getIP().equals(rPeer.getIP()) && tmpPeer.getPort() == rPeer.getPort()) ) {
                try {
                    System.out.println("[Overlay] forwarding search to " + tmpPeer.toString());
                    results.addAll(tmpPeer.search(pattern, fwdPort, fwdAddr, randomID));
                } catch (Exception e) {
                    System.out.println("[Overlay] exception while sending search to neighbour: " + e.toString());
                }
            }
        }

        return results;
    }

     public boolean isNewSearch(RemotePeer rp, int randomID) {

        boolean isNew = true;

        if (this.searchMap.containsKey(rp.toString())) {

            System.out.println("[Overlay] ok " + rp.toString() + " is already in hashmap...");

            ArrayList oldIDs = (ArrayList)this.searchMap.get(rp.toString());

            if (oldIDs.contains(randomID)) {
               System.out.println("[Overlay] ...but it's an old search request (" + randomID + ")");
               isNew = false;
            } else {
               System.out.println("[Overlay] ...but it's a new search request (" +  randomID + ")");
               oldIDs.add(randomID);
               isNew = true;
            }
        } else {
            ArrayList oldIDs = new ArrayList();
            oldIDs.add(randomID);
            this.searchMap.put(rp.toString(), oldIDs);
        }

        return isNew;
    }

    public void download(String filename, ArrayList rps, long size, javax.swing.JProgressBar pBar, ViewPeer vp, String md5) {

        boolean append = false;
        long offset = 0;
        String dstFilename = filename;
        FileRepository repo = new FileRepository(this.repoPath);

        File myFile = new File(this.repoPath + filename);

        try {
            if (myFile.length() < size) {
                append = true;
                offset = myFile.length();
            } else {
                append = false;                
            }
        } catch (Exception e) {
            System.out.println("[Overlay] shit happened: " + e.toString());
        }

        System.out.println("[Overlay] resuming file on offset " + offset);

        try {

            Iterator it = rps.iterator();
            while (it.hasNext()) {
                RemotePeer myRp = (RemotePeer)it.next();

                try {
                    vp.downloadPeer = myRp;
                    byte[] b = myRp.getFile(filename, size, offset, pBar);
                    repo.storeFileByteArray(dstFilename, b, size, append);
                    break;
                } catch (Exception e) {
                    // ignore remote peer on error
                }                
            }
            
        } catch(Exception e) {
            System.out.println("[Overlay] Could not save File: " + filename);
        }
    }

    /**
     * @return the downloadInProgress
     */
    public boolean isDownloadInProgress() {
        return downloadInProgress;
    }

    /**
     * @param downloadInProgress the downloadInProgress to set
     */
    public void setDownloadInProgress(boolean downloadInProgress) {
        this.downloadInProgress = downloadInProgress;
    }

    /**
     * @return the localAddress
     */
    public String getLocalAddress() {
        return localAddress;
    }

    /**
     * @param localAddress the localAddress to set
     */
    public void setLocalAddress(String localAddress) {
        this.localAddress = localAddress;
    }


}
